package com.beidu.lottery.call.service.portal.impl;


import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.beidu.lottery.call.constant.CacheKey;
import com.beidu.lottery.call.constant.MQTopicsConst;
import com.beidu.lottery.call.dao.ActivityLotteryPrizeMapper;
import com.beidu.lottery.call.entity.ActivityLotteryPrize;
import com.beidu.lottery.call.entity.dto.ActivityLotteryDTO;
import com.beidu.lottery.call.entity.dto.ActivityLotteryPrizeListDTO;
import com.beidu.lottery.call.entity.mq.PrizeInventoryDTO;
import com.beidu.lottery.call.entity.query.AssociatedActivityPageQuery;
import com.beidu.lottery.call.entity.vo.ActivityLotteryPrizeVO;
import com.beidu.lottery.call.errorcode.CommonCode;
import com.beidu.lottery.call.exception.ServiceException;
import com.beidu.lottery.call.service.portal.ActivityLotteryPrizeService;
import com.beidu.lottery.call.utils.ActSequenceGen;
import com.beidu.lottery.call.utils.RedisUtil;
import com.beidu.lottery.call.utils.concurrent.RedissonClientUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author tennyson
 * @since 2023/11/22 9:41
 */
@Slf4j
@Service
public class ActivityLotteryPrizeServiceImpl extends ServiceImpl<ActivityLotteryPrizeMapper, ActivityLotteryPrize> implements ActivityLotteryPrizeService {

    @Resource
    private ActivityLotteryPrizeMapper activityLotteryPrizeMapper;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private RedissonClientUtil redissonClientUtil;

    @Resource
    private KafkaTemplate<String, String> kafkaTemplate;

    @Override
    public PageInfo<ActivityLotteryPrizeListDTO> associatedActivityPage(AssociatedActivityPageQuery associatedActivityPageQuery) {
        PageHelper.startPage(associatedActivityPageQuery.getPageNum(), associatedActivityPageQuery.getPageSize());
        List<ActivityLotteryPrizeListDTO> list = activityLotteryPrizeMapper.associatedActivityPage(associatedActivityPageQuery);
        for (ActivityLotteryPrizeListDTO dto : list) {
            // 保底奖品设置库存为null
            if (ObjectUtil.isNotNull(dto.getMinimumGuarantee()) && dto.getMinimumGuarantee() == 1) {
                dto.setStock(null);
            }
        }
        return new PageInfo<>(list);
    }

    @Override
    public Integer getTotalStock(Integer prizeId) {
        return activityLotteryPrizeMapper.getTotalStock(prizeId);
    }

    @Override
    public List<ActivityLotteryPrizeVO> queryByActivityId(Integer activityId) {
        return baseMapper.queryByActivityId(activityId);
    }

    @Override
    public List<ActivityLotteryDTO> getPortalPrizesCache(Integer id) {
        // 懒加载
        return redisUtil.computeIfAbsent(CacheKey.HOTSPOT + id + ":prizes", () -> {
            List<ActivityLotteryDTO> list = baseMapper.getPortalPrizes(id);
            // 奖品库存（SQL中计算方式：设置值-已使用）
            list.forEach(p -> {
                if (p.getPrizeId() != null) {
                    redisUtil.setIfAbsent(CacheKey.genInventoryKey(id, p.getId()), p.getStock());
                }
            });
            return list;
        });
    }

    @Override
    public void incrInventoryPreSubmit(Integer id, int quantity) {
        // 组装DTO
        PrizeInventoryDTO dto = PrizeInventoryDTO.builder()
                .inventoryId(id)// 关联表id（库存表）
                .quantity(quantity).build();// 增加的数量
        dto.setBaseDTO(ActSequenceGen.genMQSeq());// MQ消息ID

        // 组装MQ消息
        JSONObject data = new JSONObject();
        data.put("dto", dto);
        log.info("组装库存使用量MQ消息完成, data => {}", data);

        // 发送Kafka消息
        try {
            kafkaTemplate.send(MQTopicsConst.ACTIVITY_LOTTERY_INVENTORY_ADD, data.toJSONString());
        } catch (Exception e) {
            log.error("消息发送异常, 增加库存使用量失败, id => {}", id, e);
            throw new ServiceException(CommonCode.LOTTERY_INVENTORY_ADD_ERROR.getMsg());
        }
    }

    @Override
    public void incrInventorySubmit(Integer id, int quantity) {
        // 使用分布式锁, 保证原子性, key：inventory:{关联表id（库存表）}
        redissonClientUtil.executeWithLock(CacheKey.INVENTORY + id, k -> {
            // 增加库存使用量
            int row = baseMapper.incrementUsedInventory(id, quantity);
            log.info("incrementUsedInventory受影响行数, row => {}", row);
        });
    }

}
